package com.chb.sso.config;

import com.alibaba.fastjson.JSON;
import com.chb.sso.annotation.LogInfoAnnotation;
import com.chb.sso.feign.LogInfoFeign;
import com.chb.sso.feign.PatientFeign;
import com.chb.sso.feign.UserFeign;
import com.chb.system.utils.IpUtils;
import com.chb.system.utils.UserAgentUtils;
import com.hos.entity.his.Patient;
import com.hos.entity.sys.LoginInfo;
import com.hos.entity.sys.User;
import com.hos.utils.JwtUtils;
import com.hos.utils.WebUtil;
import com.hos.vo.Result;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author: 陈海彬
 * @date: Created in 2023/12/7 17:23
 * @description:
 * @modified By:
 * @version:
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private UserFeign userFeign;
    @Autowired
    private PatientFeign patientFeign;
    @Resource
    private LogInfoFeign logInfoFeign;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 指定认证的service类。
        auth.userDetailsService(userDetailsService)
                // 指定密码加密器
                .passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                // 登录处理逻辑
                .loginProcessingUrl("/login")
                // 登录成功的处理器
                .successHandler(successHandler())
                // 登录失败的处理器
                .failureHandler(failureHandler())
                .permitAll();
        // 权限不足的权限处理器
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());

        // 指定运行跨域
        http.cors();
        // 指定禁用csrf
        http.csrf().disable();

        http.authorizeRequests().anyRequest().authenticated();
    }


    /**
     * @create by: 陈海彬
     * @description: 登陆成功处理器
     * @create time: 2023/12/7 17:43
     */
    @LogInfoAnnotation(login_status = "成功", login_type = "系统用户")
    private AuthenticationSuccessHandler successHandler() {
        return (request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");
            PrintWriter writer = response.getWriter();


            // 根据当前账户生成jwt令牌
            String name = authentication.getName();
            String token = JwtUtils.create(name);
            // 根据令牌把用户信息及权限放入redis中
            ValueOperations<String, String> forValue = redisTemplate.opsForValue();
            forValue.set("user:" + token, name, 30, TimeUnit.SECONDS);
            String header = request.getHeader("source-client");


            LoginInfo loginInfo = new LoginInfo();
            //  登录状态
            loginInfo.setLoginStatus("0");


            //  IP(192......)
            // UserAgentUtils.getI
            loginInfo.setIpAddr(IpUtils.getIpAddress(request));
            // 设置ip地址
            Boolean outside = IpUtils.isOutside(request);
            if (!outside) {
                loginInfo.setLoginLocation("内网IP");
            } else {
                loginInfo.setLoginLocation("公网IP");
            }
            UserAgentUtils.getBorderName(request);
            // 设置浏览器
            loginInfo.setBrowser(UserAgentUtils.getBorderName(request));
            // 设置操作系统
            loginInfo.setOs(UserAgentUtils.getOsName(request));
            //   登录时间
            loginInfo.setLoginTime(new Date());

            if (header != null) {
                // 此处为手机端登录
                Patient patient = patientFeign.selectPatientByPhone(name);
                patient.setToken(token);
                patient.setPassword("");
                //  登录账号（手机号码）
                loginInfo.setLoginAccount(patient.getPhone());
                // 用户姓名
                loginInfo.setUserName(patient.getName());
                Result<Patient> result = new Result<>(200, "登录成功", patient);
                writer.print(JSON.toJSONString(result));
                loginInfo.setLoginType("1");
            } else {
                // 此处为管理端登录
                // 查询用户信息
                User user = userFeign.selectUserByPhone(name);
                user.setToken(token);
                user.setPassword("");
                loginInfo.setUserName(user.getUserName());
                loginInfo.setLoginAccount(user.getPhone());
                Result<User> result = new Result<>(200, "登录成功", user);
                writer.print(JSON.toJSONString(result));
                //  用户类型
                loginInfo.setLoginType("0");
            }
            this.logInfoFeign.addLogInfo(loginInfo);
        };
    }

    /**
     * @create by: 陈海彬
     * @description: 登录失败处理器
     * @create time: 2023/12/7 17:43
     */
    @LogInfoAnnotation(login_status = "失败", login_type = "系统用户")
    private AuthenticationFailureHandler failureHandler() {
        return (request, response, e) -> {
            response.setContentType("application/json;charset=utf-8");
            PrintWriter writer = response.getWriter();
            // response.setStatus(508);
            Result<Void> result = new Result<>(508, "账号或密码错误", null);
            writer.print(JSON.toJSONString(result));
            writer.flush();
            writer.close();

            LoginInfo loginInfo = new LoginInfo();
            //  登录状态
            loginInfo.setLoginStatus("1");
            // 用户姓名
            loginInfo.setUserName(getUserByToken().getUserName());
            //  登录账号（手机号码）
            loginInfo.setLoginAccount(getUserByToken().getPhone());
            //  IP(192......)
            loginInfo.setIpAddr(request.getRemoteAddr());
            // 设置ip地址
            Boolean outside = IpUtils.isOutside(request);
            if (!outside) {
                loginInfo.setLoginLocation("内网IP");
            } else {
                loginInfo.setLoginLocation("公网IP");
            }
            UserAgentUtils.getBorderName(request);
            // 设置浏览器
            loginInfo.setBrowser(UserAgentUtils.getBorderName(request));
            // 设置操作系统
            loginInfo.setOs(UserAgentUtils.getOsName(request));
            //   登录时间
            loginInfo.setLoginTime(new Date());
            this.logInfoFeign.addLogInfo(loginInfo);
        };
    }

    /**
     * @create by: 陈海彬
     * @description: 权限不足处理器
     * @create time: 2023/12/7 17:43
     */
    private AccessDeniedHandler accessDeniedHandler() {
        return (request, response, e) -> {
            response.setContentType("application/json;charset=utf-8");
            PrintWriter writer = response.getWriter();
            Result<Void> result = new Result<>(401, "权限不足", null);
            writer.print(JSON.toJSONString(result));
            writer.flush();
            writer.close();
        };
    }


    /**
     * @return com.hos.entity.sys.User
     * @create by: 陈海彬
     * @description: 获取请求用户的信息
     * @create time: 2023/12/12 10:39
     */
    private User getUserByToken() {
        String token = WebUtil.getToken();
        String phone = redisTemplate.opsForValue().get("user:" + token);
        return userFeign.selectUserByPhone(phone);
    }
}